﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.Package;
using Microsoft.VisualStudio.TextManager.Interop;
using System.Runtime.InteropServices;
using EnvDTE;
using Microsoft.VisualStudio.Shell.Interop;
using NBusiness.ESharp.Compiler;
using NBusiness.ESharp;
using NBusiness.CodeDom;
using System.Collections;
using System.IO;
using Microsoft.VisualStudio;
using System.Reflection;
using VSLangProj;
using NBusiness.CodeDom.Compiler;
using System.CodeDom.Compiler;
using NBusiness.CodeDom.Reflection;
using System.Threading;
using NBusiness.VisualStudio.Commands;
using NBusiness.CodeDom.Services;

namespace NBusiness.VisualStudio
{
    /// <summary>
    /// The ESharp Language service which handles E# parsing and scanning.
    /// </summary>
    /// <see cref="http://msdn2.microsoft.com/en-us/library/bb165662.aspx"/>
    [Serializable]
    class ESharpLanguageService : LanguageService
    {
        public static ESharpLanguageService Current { get; set; }

        #region Member fields and properties
        private LanguagePreferences _preferences;
        private IScanner _scanner;
        private DTE _dte;

        Dictionary<IVsTextLines, ESharpColorizer> _colorizers = new Dictionary<IVsTextLines, ESharpColorizer>();
        Dictionary<string, ProjectParseInfo> _projectParsers = new Dictionary<string, ProjectParseInfo>();
        
        public override string Name
        {
            get { return Resources.ESharpLanguageServiceName; }
        }

        private static ESharpColorableItem[] colorableItems = {
            // The first 6 items in this list MUST be these default items.
            new ESharpColorableItem("Keyword", COLORINDEX.CI_BLUE, COLORINDEX.CI_USERTEXT_BK),
            new ESharpColorableItem("Comment", COLORINDEX.CI_DARKGREEN, COLORINDEX.CI_USERTEXT_BK),
            new ESharpColorableItem("Identifier", COLORINDEX.CI_AQUAMARINE, COLORINDEX.CI_USERTEXT_BK),
            new ESharpColorableItem("String", COLORINDEX.CI_MAROON, COLORINDEX.CI_USERTEXT_BK),
            new ESharpColorableItem("Number", COLORINDEX.CI_SYSPLAINTEXT_FG, COLORINDEX.CI_USERTEXT_BK),
            new ESharpColorableItem("Text", COLORINDEX.CI_SYSPLAINTEXT_FG, COLORINDEX.CI_USERTEXT_BK),
            new ESharpColorableItem("Tokens", COLORINDEX.CI_PURPLE, COLORINDEX.CI_USERTEXT_BK),
            new ESharpColorableItem("Unknown", COLORINDEX.CI_BLACK, COLORINDEX.CI_LIGHTGRAY)
        };


        #endregion

        #region Constructors

        public ESharpLanguageService(DTE dte)
        {
            _dte = dte;
        }

        #endregion

        #region Get Format Filter List
        /// <summary>
        /// This method supplies a list of file extension filters formatted for a Save As dialog box.
        /// </summary>
        /// <returns>Resources.ESharpFormatFilterList</returns>
        /// <see cref="http://msdn2.microsoft.com/en-us/library/microsoft.visualstudio.package.languageservice.getformatfilterlist.aspx"/>
        public override string GetFormatFilterList()
        {
            return Resources.ESharpFormatFilterList;
        } 
        #endregion

        #region Get Language Preferences

        public override LanguagePreferences GetLanguagePreferences()
        {
            if (_preferences == null)
            {
                _preferences = new LanguagePreferences(
                    this.Site,
                    typeof(ESharpLanguageService).GUID,
                    this.Name);
                _preferences.Init();
                
            }
            return _preferences;

        } 

        #endregion

        #region Get Scanner
        /// <summary>
        /// This method returns an instance of an IScanner object that 
        /// implements a line-oriented parser or scanner used for obtaining 
        /// tokens and their types and triggers.
        /// </summary>
        /// <param name="buffer"></param>
        /// <returns></returns>
        public override IScanner GetScanner(IVsTextLines buffer)
        {
            if (_scanner == null)
            {             
                _scanner = new ESharpScanner(this, buffer);
            }



            return _scanner;
        } 
        #endregion

        #region Parse Source
        private ProjectParseInfo GetParseInfo(Project project)
        {
            if (project == null)
                throw new ArgumentNullException("proj");

            if (!_projectParsers.ContainsKey(project.UniqueName))
            {
                ESharpProvider provider = new ESharpProvider();
                ESharpReflectionService reflector = new ESharpReflectionService(project);
                ESharpFrameworkService frameworkService = new ESharpFrameworkService(reflector);
                provider.SetService(typeof(IFrameworkService), frameworkService);

                _projectParsers.Add(project.UniqueName, new ProjectParseInfo { Provider = provider });
            }

            return _projectParsers[project.UniqueName];
        }

        private void RefreshRoot(Project project)
        {
            ProjectParseInfo info = GetParseInfo(project);
            EntityFile[] files = GetFiles(project);

            CommandManager.Queue(new RefreshServiceRoot(info, files));
        }

        public override AuthoringScope ParseSource(ParseRequest req)
        {
            if (req == null)
                throw new ArgumentNullException("req");

            ProjectItem item = _dte.Solution.FindProjectItem(req.FileName);
            Project project = item.ContainingProject;

            ProjectParseInfo parseInfo = GetParseInfo(project);
            ESharpScope scope = new ESharpScope(parseInfo, req);

            return scope;
        }

        public override Source CreateSource(IVsTextLines buffer)
        {
            object[] projects = (_dte.ActiveSolutionProjects as object[]);
            foreach(Project project in projects)
                RefreshRoot(project);

            ESharpSource source = new ESharpSource(this, buffer, GetColorizer(buffer));
            source.Changed += new EventHandler(source_Changed);

            return source;
        }

        void source_Changed(object sender, EventArgs e)
        {
            ESharpSource source = sender as ESharpSource;
            ProjectItem item = _dte.Solution.FindProjectItem(source.GetFilePath());
            
            Project project = item.ContainingProject;
            RefreshRoot(project);
        }

        #endregion

        #region Get Colorizer
        public override Colorizer GetColorizer(IVsTextLines buffer)
        {
            ESharpScanner scanner = GetScanner(buffer) as ESharpScanner;
            ESharpColorizer colorizer = new ESharpColorizer(this, buffer, scanner);

            return colorizer;
        }

        public override int GetItemCount(out int count)
        {
            count = colorableItems.Length;
            return VSConstants.S_OK;
        }

        public override int GetColorableItem(int index, out IVsColorableItem item)
        {
            if (index < 1)
                throw new ArgumentOutOfRangeException("index");
            
            item = colorableItems[index - 1];
            return VSConstants.S_OK;
        }
        #endregion

        #region Get Root

        public EntityFile[] GetFiles(Project project)
        {
            List<EntityFile> items = new List<EntityFile>();
            foreach (ProjectItem item in project.ProjectItems)
            {
                items.AddRange(item.GetESharpProjectItems());
            }
            return items.ToArray();
        }

        #endregion

        public void UpdateReflectionInformation(CodeDomProvider provider)
        {
            //DTE dte = GetService(typeof(DTE)) as DTE;
            //Project project = dte.ActiveDocument.ProjectItem.ContainingProject;

            //string itemName = @"Properties\EntityReflection." + provider.FileExtension;
            //string path = (string)project.Properties.Item("LocalPath").Value;
            //path = Path.Combine(Path.GetDirectoryName(path), itemName);

            //ProjectItem reflectionItem = null;
            //if (!File.Exists(path))
            //{
            //    File.Create(path);
            //    reflectionItem = project.ProjectItems.AddFromFile(path);
            //}
            //else reflectionItem = project.ProjectItems.Item("Properties").ProjectItems.Item("EntityReflection." + provider.FileExtension);

            //foreach (CodeElement element in reflectionItem.FileCodeModel.CodeElements.Cast<CodeElement>())
            //{
            //    if (element is CodeAttribute)
            //        (element as CodeAttribute).Delete();
            //}

            //CodeAttribute attribute = reflectionItem.FileCodeModel.AddAttribute(typeof(EntityFamilyAttribute).FullName, "", 1);

            //reflectionItem.Save(path);
        }
    }

    internal class ProjectParseInfo
    {
        public ESharpProvider Provider { get; set; }
        public EntityRoot Root { get; set; }
        public EntityElement[] Elements { get; set; }
    }
}
